use std::{
    cell::UnsafeCell,
    fmt::{Debug, Display},
    mem::ManuallyDrop,
    ops::{Deref, DerefMut},
};

/// must use `init`
/// please at program start before use this fn
pub struct StaticTypeForStd<T, F = fn() -> T> {
    val: UnsafeCell<Option<T>>,
    is_init_lock: spin::Mutex<bool>,
    call_fun: UnsafeCell<ManuallyDrop<F>>,
}

impl<T, F: FnOnce() -> T> StaticTypeForStd<T, F> {
    pub const fn new(init_fun: F) -> StaticTypeForStd<T, F> {
        Self {
            val: UnsafeCell::new(None),
            is_init_lock: spin::Mutex::new(false),
            call_fun: UnsafeCell::new(ManuallyDrop::new(init_fun)),
        }
    }

    pub fn init_static(&self) {
        let mut lock = self.is_init_lock.lock();
        if !*lock {
            unsafe {
                let f = ManuallyDrop::take(&mut *self.call_fun.get());
                *self.val.get() = Some(f());
            }
            *lock = true;
        } else {
            // warn!("StaticType is already initialized");
        }
    }
}

impl<T, F> StaticTypeForStd<T, F> {
    #[must_use]
    pub fn is_init(&self) -> bool {
        *self.is_init_lock.lock()
    }

    /// this fn not safed; please before use this fn must use `init_static`
    #[inline(always)]
    pub fn get_static(&self) -> Option<&T> {
        unsafe { &*self.val.get() }.as_ref()
    }

    /// this fn not safed; please before use this fn must use `init_call` or `get_or_init`
    #[inline(always)]
    pub fn get_static_unchecked(&self) -> &T {
        self.get_static().unwrap_or_else(|| {
            unreachable!("StaticType not set value, Please is use static_val.init()")
        })
    }

    /// this fn not safed; please before use this fn must use `init_call` or `get_or_init`
    #[inline(always)]
    pub fn get_mut(&self) -> &mut Option<T> {
        unsafe { &mut *self.val.get() }
    }

    #[inline(always)]
    pub fn get_safe(&self) -> Option<&T> {
        if !*self.is_init_lock.lock() {
            return None;
        }
        unsafe { &*self.val.get() }.as_ref()
    }

    // 强制drop内存, 请确保在此时没有使用此内存
    // force drop memory, please ensure that the memory is not used before this
    pub fn force_drop(&self) -> Option<T> {
        *self.is_init_lock.lock() = false;
        self.get_mut().take()
    }
}

impl<T, F> Deref for StaticTypeForStd<T, F> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        self.get_static_unchecked()
    }
}

// impl derefmut
impl<T, F: FnOnce() -> T> DerefMut for StaticTypeForStd<T, F> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.get_mut()
            .as_mut()
            .unwrap_or_else(|| unreachable!("StaticType not set"))
    }
}

impl<T, F> AsRef<T> for StaticTypeForStd<T, F> {
    fn as_ref(&self) -> &T {
        self.get_static_unchecked()
    }
}

// impl send and sync
// focer thread safe
unsafe impl<T, F> Send for StaticTypeForStd<T, F> {}
unsafe impl<T, F> Sync for StaticTypeForStd<T, F> {}

impl<T: Debug, F: FnOnce() -> T> Debug for StaticTypeForStd<T, F> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let val = self.get_static_unchecked();
        f.write_fmt(format_args!("{val:?}"))
    }
}

impl<T: Display, F> Display for StaticTypeForStd<T, F> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let val = self.get_static().map(|x| format!("{x}"));
        let val = val.unwrap_or_else(|| "None".to_string());
        f.write_str(&val)
    }
}

#[test]
fn test_static_type() {
    //定义静态变量
    static STATIC_TYPE: StaticTypeForStd<i32> = StaticTypeForStd::new(|| {
        let r = 11 + 11;
        println!("init_fun: {}", r);
        r
    });

    // 没有初始化之前
    assert_eq!(format!("{}", STATIC_TYPE), "None");

    // panic
    assert_eq!(STATIC_TYPE.is_init(), false);
    // assert_eq!(*STATIC_TYPE, 22); //panic

    // 初始化数据
    STATIC_TYPE.init_static();

    // 第二次初始化数据, 预期不执行
    STATIC_TYPE.init_static();

    // 控制台输出相应的数据
    assert_eq!(format!("{}", STATIC_TYPE), "22");

    // 获取数据,数据相同
    assert_eq!(*STATIC_TYPE, 22);
}
